﻿using System;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using JmShared;
using System.Linq;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Collections.Generic;
using JiveMessenger.Utilities;
using GalaSoft.MvvmLight.Messaging;
using System.ComponentModel;
using System.Windows.Data;
using GalaSoft.MvvmLight.Command;
using JiveMessenger.User;
using System.Diagnostics;
using NLog;
using JiveMessenger.Misc;
using System.Reflection;

namespace JiveMessenger.ViewModels
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class ViewModelNotifications : ViewModelBase, IViewModelNotifications
    {
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
        private IClient _currentClient;
        public RelayCommand AcceptInviteCommand { get; set; }
        public RelayCommand DeclineInviteCommand { get; set; }

        private string _acceptButtonText;
        public string AcceptButtonText
        {
            get { return _acceptButtonText; }
            set
            {
                if (_acceptButtonText != value)
                {
                    Set(() => AcceptButtonText, ref _acceptButtonText, value);
                }
            }
        }

        private string _declineButtonText;
        public string DeclineButtonText
        {
            get { return _declineButtonText; }
            set
            {
                if (_declineButtonText != value)
                {
                    Set(() => DeclineButtonText, ref _declineButtonText, value);
                }
            }
        }


        private ObservableCollection<NotificationBase> _notifications;
        public ObservableCollection<NotificationBase> Notifications
        {
            get
            {
                if (_notifications == null)
                {
                    this.Notifications = new ObservableCollection<NotificationBase>();
                    ICollectionView view = CollectionViewSource.GetDefaultView(this.Notifications) as ICollectionView;
                    view.GroupDescriptions.Add(new PropertyGroupDescription("NotificationType"));
                }
                return _notifications;
            }
            set
            {
                if (_notifications != value)
                {
                    if (_notifications != null)
                        _notifications.CollectionChanged -= Notifications_CollectionChanged;
                    Set(() => Notifications, ref _notifications, value);
                    if (_notifications != null)
                        _notifications.CollectionChanged += Notifications_CollectionChanged;
                }
            }
        }

        public int NotificationCount
        {
            get
            {
                return this.Notifications != null ? this.Notifications.Count : 0;
            }
        }


        private bool _haveUnreadNotifications;
        public bool HaveUnreadNotifications
        {
            get { return _haveUnreadNotifications; }
            set
            {
                if (_haveUnreadNotifications != value)
                {
                    Set(() => HaveUnreadNotifications, ref _haveUnreadNotifications, value);
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the ViewModelNotifications class.
        /// </summary>
        public ViewModelNotifications(IClient currentClient)
        {
            _currentClient = currentClient;
            this.AcceptButtonText = "Accept";
            this.DeclineButtonText = "Decline";
            this.AcceptInviteCommand = new RelayCommand(AcceptInviteCommandAction, AcceptCommandCanExecute);
            this.DeclineInviteCommand = new RelayCommand(DeclineInviteCommandAction, DeclineCommandCanExecute);


            Messenger.Default.Register<NotificationMessage<IEnumerable<NotificationBase>>>(this, msg =>
                {
                    switch (msg.Notification)
                    {
                        case "AddNotificationList":
                            {
                                Notifications.Clear();
                                foreach (var n in msg.Content)
                                {
                                    Notifications.Add(n);
                                }
                                break;
                            }
                    }
                });
            Messenger.Default.Register<NotificationMessage<NotificationBase>>(this, msg =>
                {
                    switch (msg.Notification)
                    {
                        case "AddNotification":
                            this.Notifications.Add(msg.Content);
                            break;
                    }
                });
            Messenger.Default.Register<NotificationMessage>(this, msg =>
            {
                switch (msg.Notification)
                {
                    case "MarkAllNotificationsAsRead":
                        foreach (NotificationBase n in Notifications)
                            n.IsRead = true;
                        HaveUnreadNotifications = false;
                        var notificationList = new NotificationList()
                        {
                            FriendInviteNotifications = Notifications.Where(x => x is FriendInviteNotification).Cast<FriendInviteNotification>(),
                            GroupInviteNotifications = Notifications.Where(x => x is GroupInviteNotification).Cast<GroupInviteNotification>(),
                            ServerMessageNotifications = Notifications.Where(x => x is ServerMessageNotification).Cast<ServerMessageNotification>(),
                            UpdateAvailableNotifications = Notifications.Where(x => x is UpdateAvailableNotification).Cast<UpdateAvailableNotification>()
                        };
                        _currentClient.Connection.SendCommand(notificationList, ClientCommands.NotificationsUpdate);
                        break;
                }
            });
            Messenger.Default.Register<NotificationMessage<Updater>>(this, msg =>
            {
                switch (msg.Notification)
                {
                    case "NewApplicationVersionFound":
                        App.Current.Dispatcher.Invoke(() =>
                            {
                                if (_currentClient.IsLoggedIn && !this.Notifications.Any(x => x is UpdateAvailableNotification))
                                {
                                    var notification = new UpdateAvailableNotification()
                                    {
                                        NotificationText = string.Format("New version of Jive Messenger found: {0}. Your current version is {1}.", msg.Content.VersionNumber, Assembly.GetExecutingAssembly().GetName().Version.ToString()),
                                        DownloadUrl = msg.Content.DownloadUrl,
                                        Version = msg.Content.VersionNumber
                                    };
                                    this.Notifications.Add(notification);
                                }
                            });
                        break;
                }
            });
        }
        private bool AcceptCommandCanExecute()
        {
            if (this.Notifications == null || this.NotificationCount == 0 || !this.Notifications.Any(x => x.IsSelected))
                return false;

            else if (!this.Notifications.Where(x => x.IsSelected).Any(x => x.NotificationType != NotificationType.FriendInvite && x.NotificationType != NotificationType.GroupInvite))
            {
                this.AcceptButtonText = "Accept";
                this.DeclineButtonText = "Decline";
                return true;
            }
            else if (this.Notifications.Where(x => x.IsSelected).Any(x => x.NotificationType == NotificationType.ServerMessage))
            {
                this.AcceptButtonText = "Dismiss";
                this.DeclineButtonText = "Decline";
                return true;
            }
            else if (this.Notifications.Where(x => x.IsSelected).Any(x => x.NotificationType == NotificationType.UpdateAvailable))
            {
                this.AcceptButtonText = "Download";
                this.DeclineButtonText = "Skip version";
                return true;
            }
            this.AcceptButtonText = "Accept";
            this.AcceptButtonText = "Decline";
            return false;
        }

        private bool DeclineCommandCanExecute()
        {
            if (this.Notifications == null || this.NotificationCount == 0)
                return false;

            if (!this.Notifications.Any(x => x.IsSelected))
                return false;
            else if (this.Notifications.Where(x => x.IsSelected).Any(x => x.NotificationType == NotificationType.FriendInvite
                || x.NotificationType == NotificationType.GroupInvite
                || x.NotificationType == NotificationType.UpdateAvailable))
                return true;
            else if (this.Notifications.Where(x => x.IsSelected).Any(x => x.NotificationType == NotificationType.ServerMessage))
                return false;

            return false;
        }
        private void DeclineInviteCommandAction()
        {
            var selectedNotifications = new List<NotificationBase>(this.Notifications.Where(x => x.IsSelected));
            foreach (NotificationBase notification in selectedNotifications)
            {
                IClient currentClient = _currentClient;
                switch (notification.NotificationType)
                {
                    case NotificationType.FriendInvite:
                        currentClient.Connection.SendMessage(new ChatMessage(currentClient.CurrentUserInfo.ID, currentClient.CurrentUserInfo.UserName, MessageType.DeclineFriendRequest) { RecipientID = ((FriendInviteNotification)notification).SenderUsername });
                        break;
                    case NotificationType.GroupInvite:
                        currentClient.Connection.SendMessage(new ChatMessage(currentClient.CurrentUserInfo.ID, currentClient.CurrentUserInfo.UserName, MessageType.DeclineGroupInvite) { RecipientID = ((GroupInviteNotification)notification).Group.ID });
                        break;
                    default:
                    case NotificationType.ServerMessage:
                        currentClient.Connection.SendMessage(new ChatMessage(currentClient.CurrentUserInfo.ID, currentClient.CurrentUserInfo.UserName, MessageType.DeclineGroupInvite) { RecipientID = ((GroupInviteNotification)notification).Group.ID });
                        break;
                    case NotificationType.UpdateAvailable:
                        currentClient.SkipUpdateVersion(((UpdateAvailableNotification)notification).Version);
                        break;
                }

                this.Notifications.Remove(notification);

            }
        }
        private void AcceptInviteCommandAction()
        {
            var selectedNotifications = new List<NotificationBase>(this.Notifications.Where(x => x.IsSelected));
            foreach (NotificationBase notification in selectedNotifications)
            {
                IClient currentClient = _currentClient;
                switch (notification.NotificationType)
                {
                    case NotificationType.FriendInvite:
                        currentClient.Connection.SendMessage(new ChatMessage(currentClient.CurrentUserInfo.ID, currentClient.CurrentUserInfo.UserName, MessageType.AcceptFriendRequest) { RecipientID = ((FriendInviteNotification)notification).SenderUsername });
                        break;
                    case NotificationType.GroupInvite:
                        currentClient.Connection.SendMessage(new ChatMessage(currentClient.CurrentUserInfo.ID, currentClient.CurrentUserInfo.UserName, MessageType.AcceptGroupInvite) { RecipientID = ((GroupInviteNotification)notification).Group.ID });
                        break;
                    case NotificationType.ServerMessage:
                        currentClient.Connection.SendMessage(new ChatMessage(currentClient.CurrentUserInfo.ID, currentClient.CurrentUserInfo.UserName, MessageType.DismissServerMessageNotification) { RecipientID = ((ServerMessageNotification)notification).ID });
                        break;
                    case NotificationType.UpdateAvailable:
                        currentClient.DownloadApplicationUpdate(((UpdateAvailableNotification)notification).DownloadUrl);
                        break;
                    case NotificationType.Other:

                        break;
                }

                this.Notifications.Remove(notification);
            }
        }

        void Notifications_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            this.HaveUnreadNotifications = ((ObservableCollection<NotificationBase>)sender).Any(x => !x.IsRead);
            RaisePropertyChanged("Notifications");
            RaisePropertyChanged("NotificationCount");
        }

        public override void Cleanup()
        {
            App.Current.Dispatcher.Invoke(() => this.Notifications.Clear());
        }
    }
}